package com.derbysoft.synxisadapter.dswitch2.service.impl;

import com.derbysoft.remote.dswitch.core.*;
import com.derbysoft.remote.dswitch.generic.constants.UniqueIDType;
import com.derbysoft.remote.dswitch.hotel.dto.CancelRQ;
import com.derbysoft.remote.dswitch.hotel.dto.CancelRS;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTACancelRQ;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTACancelRS;
import com.derbysoft.synxisadapter.commons.exception.BusinessException;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.commons.model.Reservation;
import com.derbysoft.synxisadapter.commons.repository.ReservationRepository;
import com.derbysoft.synxisadapter.commons.utils.Constant;
import com.derbysoft.synxisadapter.commons.utils.ErrorMessageUtils;
import com.derbysoft.synxisadapter.dswitch2.translator.impl.TranslateHelper;
import com.derbysoft.synxisadapter.webservice.impl.ResponsePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

public class CancelServiceImpl extends AbstractHotelService<GenericRQ<CancelRQ>, GenericRS<CancelRS>, OTACancelRQ, OTACancelRS> {
    private static Logger logger = LoggerFactory.getLogger(CancelServiceImpl.class);
    private ReservationRepository reservationRepository;

    @Override
    public GenericRS<CancelRS> invoke(GenericRQ<CancelRQ> rq) {
        GenericRS<CancelRS> response = new GenericRS<CancelRS>();
        String ersp = TranslateHelper.find(rq.getPayload().getHotelReservationIDs(), UniqueIDType.ERSP);
        try {
            Channel channel = loadChannel(rq);

            List<OTACancelRQ> requestList = requestTranslator.translate(rq, null);

            List<OTACancelRS> responseList = new ArrayList<OTACancelRS>();

            List<ResponsePair<OTACancelRQ, OTACancelRS>> responsePairs = providerWebService.batchCancel(requestList, channel);

            for (ResponsePair<OTACancelRQ, OTACancelRS> responsePair : responsePairs) {
                OTACancelRS otaCancelRS = responsePair.getResponse();
                if (hasError(otaCancelRS)) {
                    reservationRepository.updateStatus(ersp, Reservation.Status.CANCEL_FAILED);
                    throw new IllegalStateException(otaCancelRS.getErrors().getError().get(0).getShortText());
                }
                responseList.add(otaCancelRS);
                cancelReservationItem(ersp, responsePair.getRequest().getUniqueID().get(0).getID(),
                        otaCancelRS.getCancelInfoRS().getUniqueID().getID());
            }

            response = responseTranslator.translate(responseList, rq);

            response.setSuccess(new SuccessDTO());

            reservationRepository.updateStatus(ersp, Reservation.Status.CANCELLED);

        } catch (Exception ex) {
            logger.error(ErrorMessageUtils.buildErrorMessage(ex));
            response.setErrors(createErrors(ex, ErrorMessageUtils.buildErrorMessage(ex)));
            reservationRepository.updateStatus(ersp, Reservation.Status.CANCEL_FAILED);
        }
        response.setHeader(createHeader(rq));
        return response;
    }

    private ErrorsDTO createErrors(Throwable e, String stackTrace) {
        ErrorsDTO errorsDTO = new ErrorsDTO();
        ArrayList<ErrorDTO> errorDTOs = new ArrayList<ErrorDTO>();
        if (BusinessException.class.isAssignableFrom(e.getClass())) {
            BusinessException businessException = (BusinessException) e;
            errorDTOs.add(new ErrorDTO(businessException.getErrorCode(),
                    businessException.getMessage(), Constant.SYNXIS_SOURCE));
        } else {
            errorDTOs.add(new ErrorDTO(Constant.SYSTEM_ERROR, stackTrace, Constant.SYNXIS_SOURCE));
        }
        errorsDTO.setErrorList(errorDTOs);
        return errorsDTO;
    }

    private void cancelReservationItem(String ersp, String confirmNumber, String cancelNumber) {
        reservationRepository.cancel(ersp, confirmNumber, cancelNumber);
    }

    public void setReservationRepository(ReservationRepository reservationRepository) {
        this.reservationRepository = reservationRepository;
    }
}
